package org.example.mq.dataCenter;

import org.example.mq.common.MqException;
import org.example.mq.mqserver.core.Binding;
import org.example.mq.mqserver.core.Exchange;
import org.example.mq.mqserver.core.MSGQueue;
import org.example.mq.mqserver.core.Message;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class MemoryDataCenter {
//    管理交换机
    private ConcurrentHashMap<String, Exchange> exchangeMap = new ConcurrentHashMap<>();
//    管理队列
    private ConcurrentHashMap<String, MSGQueue> msgQueueMap = new ConcurrentHashMap<>();
//    管理绑定关系(第一个key是交换机名字，第二个key是队列名字)其实知道binding之后就知道了exchange和queue
    private ConcurrentHashMap<String,ConcurrentHashMap<String, Binding>> bindingsMap = new ConcurrentHashMap<>();
//    管理消息(key是messageId)
    private ConcurrentHashMap<String, Message> messageMap = new ConcurrentHashMap<>();
//    队列下的所有消息（第一个key是队列名，value是消息）
    private ConcurrentHashMap<String, LinkedList<Message>> queueMessageMap = new ConcurrentHashMap<>();
//    队列下的没有被应答的消息(第一个key是队列名字，第二个key是messageId)这里使用hashMap可以快速的进行修改
    private ConcurrentHashMap<String,ConcurrentHashMap<String,Message>> queueMessageWaitMap = new ConcurrentHashMap<>();

    /*
    * 针对内存上交换机操作
    * */
    public void insertExchange(String exchangeName,Exchange exchange){
        exchangeMap.put(exchangeName, exchange);
        System.out.println("[MemoryDataCenter] 交换机添加成功");
    }

    public void deleteExchange(String exchangeName){
        exchangeMap.remove(exchangeName);
        System.out.println("[MemoryDataCenter] 交换机删除成功");
    }

    public Exchange getExchange(String exchangeName){
        return exchangeMap.get(exchangeName);
    }

    /*
    * 针对内存上的队列操作
    * */
    public void insertQueue(String queueName,MSGQueue queue){
        msgQueueMap.put(queueName, queue);
        System.out.println("[MemoryDataCenter] 队列添加成功");
    }

    public void deleteQueue(String queueName){
        msgQueueMap.remove(queueName);
        System.out.println("[MemoryDataCenter] 队列删除成功");
    }

    public MSGQueue getQueue(String queueName){
        return msgQueueMap.get(queueName);
    }

    /*
    * 针对内存上绑定关系操作
    * */
    public void insertBinding(Binding binding) throws MqException {
//        1.检查这个交换机的value是否存在，如果不存在则需要创建(根据key检查value是否存在)
/*        ConcurrentHashMap<String, Binding> bindMap = bindingsMap.get(binding.getExchangeName());
        if(bindMap == null){
            bindMap = new ConcurrentHashMap<>();
            bindingsMap.put(binding.getExchangeName(),bindMap);
        }*/
        //另一种写法
        ConcurrentHashMap<String, Binding> bindMap = bindingsMap.computeIfAbsent(binding.getExchangeName(),
                k -> new ConcurrentHashMap<>());

        synchronized (bindMap) {
//        2.如果根据交换机名和队列名，查询出来的关系存在，则抛出异常，因为已经两者之间已经存在关系，所以不能插入
            if (bindMap.get(binding.getQueueName()) != null) {
                throw new MqException("[MemoryDataCenter] 绑定关系已经存在，不能继续绑定");
            }
            bindMap.put(binding.getQueueName(), binding);
            System.out.println("[MemoryDataCenter] 绑定添加成功");
        }
    }

    //获取唯一的一个绑定（一层一层查询）
    public Binding getBinding(String exchangeName ,String queueName){
        ConcurrentHashMap<String, Binding> bindingMap = bindingsMap.get(exchangeName);
        //判断最外层value是否为空
        if(bindingMap==null){
            return null;
        }
        return bindingMap.get(queueName);
    }

    //获取这个交互机的所有绑定
    public ConcurrentHashMap<String,Binding> getBindings(String exchangeName){
        return bindingsMap.get(exchangeName);
    }

    //删除绑定
    public void deleteBinding(Binding binding) throws MqException {
        ConcurrentHashMap<String,Binding> bindingMap = bindingsMap.get(binding.getExchangeName());
        if(bindingMap == null){
            throw new MqException("[MemoryDataCenter]  绑定关系不存在,无法删除");
        }
        bindingMap.remove(binding.getQueueName());
        System.out.println("[MemoryDataCenter] 绑定删除成功");
    }

    /*
    * 内存上的消息管理
    * */
    //添加消息
    public void insertMessage(Message message){
        messageMap.put(message.getMessageId(),message);
        System.out.println("[MemoryDataCenter] 添加消息成功");
    }
    //根据messageID查询消息
    public Message getMessage(String messageId){
        return messageMap.get(messageId);
    }
    //根据messageId删除消息
    public void deleteMessage(String messageId) throws MqException {
        Message message = messageMap.get(messageId);
        if(message==null){
            throw new MqException("[MemoryDataCenter] 消息不存在，无法删除");
        }
        messageMap.remove(messageId);
        System.out.println("[MemoryDataCenter] 删除消息成功");
    }

    //发送消息到指定队列
    public void sendMessage(MSGQueue queue,Message message){
        //computeIfAbsent是线程安全的
        LinkedList<Message> messages = queueMessageMap.computeIfAbsent(queue.getName(),k->new LinkedList<>());
        synchronized (messages){
            messages.add(message);
        }
        //在队列中添加，在总的消息管理中也需要添加
        //因为messageID相同，所有也不用担心内容不一样
        insertMessage(message);
        System.out.println("[MemoryDataCenter] 消息成功放入队列中");
    }
    //从队列中取出消息
    public Message pollMessage(String queueName){
        LinkedList<Message> messages = queueMessageMap.get(queueName);
        //判断队列中有没有消息
        if(messages==null){
            return null;
        }
        synchronized (messages){
            if(messages.size()==0){
                return null;
            }
            //采用从头取出（头插法）
            Message curmessage = messages.remove(0);
            System.out.println("[MemoryDataCenter] 消息成功从队列中取出");
            return curmessage;
        }
    }

    //获取指定队列中消息的个数
    public int getQueueMessageCount(String queueName){
        LinkedList<Message> messages = queueMessageMap.get(queueName);
        //判断队列中有没有消息
        if(messages == null){
            return 0;
        }
        synchronized (messages){
            return messages.size();
        }
    }

    /*
    * 未处理消息管理
    * */
    //添加未处理的消息
    public void insertMessageWaitAck(MSGQueue queue,Message message) {
        ConcurrentHashMap<String, Message> messageWaitAckMap = queueMessageWaitMap.computeIfAbsent(queue.getName(), k -> new ConcurrentHashMap<>());
        messageWaitAckMap.put(message.getMessageId(), message);
        System.out.println("[MemoryDataCenter] 未处理消息添加成功");
    }
    //删除未确认的消息
    public void deleteMessageWaitAck(String queueName,String messageId){
        ConcurrentHashMap<String, Message> messageWaitAckMap = queueMessageWaitMap.get(queueName);
        if(messageWaitAckMap==null){
            return;
        }
        messageWaitAckMap.remove(messageId);
        System.out.println("[MemoryDataCenter] 未处理消息删除成功");
    }
    //获取指定的未确认消息
    public Message getMessageWaitAck(String queueName,String messageId){
        ConcurrentHashMap<String, Message> messageWaitAckMap = queueMessageWaitMap.get(queueName);
        if(messageWaitAckMap==null){
            return null;
        }
        return messageWaitAckMap.get(messageId);
    }


    /*
    * 将硬件的数据全部加载到内存中
    * */
    public void recovery(DiskDataCenter dataCenter) throws IOException, MqException, ClassNotFoundException {
//        1.清空之前的数据
        exchangeMap.clear();
        msgQueueMap.clear();
        bindingsMap.clear();
        messageMap.clear();
        queueMessageWaitMap.clear();
//        2.加载交换机数据
        List<Exchange> exchanges = dataCenter.selectAllExchange();
        for (Exchange exchange :exchanges){
            exchangeMap.put(exchange.getName(),exchange);
        }
//        3.加载队列数据
        List<MSGQueue> msgQueues = dataCenter.selectAllMSGQueue();
        for (MSGQueue queue:msgQueues){
            msgQueueMap.put(queue.getName(),queue);
        }
//        4.加载绑定关系(将一个个绑定添加进里面)
        //先检查交互机是否存在，不存在则创建对应的hashMap,然后添加其中的内容
        List<Binding> bindings = dataCenter.selectAllBinding();
        for (Binding binding:bindings){
            ConcurrentHashMap<String, Binding> bindingMap = bindingsMap.computeIfAbsent(binding.getExchangeName(), k -> new ConcurrentHashMap<>());
            bindingMap.put(binding.getQueueName(), binding);
        }
//        5.恢复所有的消息数据(添加每一个队列中的消息)(这里有两个，1.管理所有的消息，2.管理队列下的消息)
        for(MSGQueue queue: msgQueues){
            LinkedList<Message> messages = dataCenter.selectAllMessage(queue);
            //队列下的消息
            queueMessageMap.put(queue.getName(), messages);
            for (Message message:messages){
                messageMap.put(message.getMessageId(),message);
            }
        }
//        6.未被确认的消息
        //这里未被确认的消息，并不会被加载进入内存，考虑情况：服务器如果重启，那么未被确认的消息，再一次被加载，实际上被当做没有取出的消息
        //这时候只需要重新取出未被取出的消息，那么和方法5（加载数据）是一样的
    }
}
